<template>
</template>

<script setup>
  import * as THREE from "three";
  import * as d3geo from "d3-geo";
  import * as TWEEN from '@tweenjs/tween.js'
  import { ref, onMounted } from 'vue';
  import xwqJson from '@/assets/json/xwq.json'
  import xwqBroundaryJson from '@/assets/json/xwq-broundary.json'
  import nmgJson from '@/assets/json/nmg.json'
  import xlglmJson from '@/assets/json/xlglm.json'
  import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
  import { CSS2DRenderer, CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer'

  const center = [117.7179630504896, 44.61734576753193]
  // 墨卡托投影转换
  const projection = d3geo.geoMercator().center(center).scale(500).translate([0, 0])
  const mapEdgeLightObj = {
    mapEdgePoints: [],
    lightOpacityGeometry: null,  // 单独把geometry提出来，动画用

    // 边缘流光参数
    lightSpeed: 3,
    lightCurrentPos: 0,
    lightOpacitys: null,          
  }

  // 场景
  const scene = new THREE.Scene()
  // scene.background = new THREE.TextureLoader().load('images/bgi.png');

  // 相机
  const camera = new THREE.PerspectiveCamera(80, window.innerWidth / window.innerHeight, 0.01, 1000)
  camera.position.set(0, -10, 15)

  // 环境光
  const ambient = new THREE.AmbientLight(0x06dcf8, 5.0)
  scene.add(ambient)

  // 渲染器
  const renderer = new THREE.WebGLRenderer({ antialias: true })
  renderer.setSize(window.innerWidth, window.innerHeight)
  renderer.setClearColor(0x0F1427)
  document.body.appendChild(renderer.domElement)

  // css 2d 渲染器
  let labelRenderer = new CSS2DRenderer()
  labelRenderer.setSize(window.innerWidth, window.innerHeight)
  labelRenderer.domElement.style.position = 'absolute'
  labelRenderer.domElement.style.top = '0px'
  labelRenderer.domElement.style.pointerEvents = 'none'
  document.body.appendChild(labelRenderer.domElement)

  let clock = new THREE.Clock()

  const texture = new THREE.TextureLoader()

  var hoverIntersects = []
  var clickIntersects = []
  
  const img1 = ref()
  const img2 = ref()
  const uniforms = { iTime: { value: 0.1 } }

  const bufferGeometry = new THREE.BufferGeometry()

  const lines = []
  let positions = null
  let opacitys = null
  let indexBol = true
  let points = []

  onMounted(() => {
    initModel()

    loop()
  })

  const initModel = () => {
    init3DMap()

    initCirclePoint(scene, 30)
    img1.value = initRotating1(scene, 40)
    img2.value = initRotating2(scene, 20)
    initSceneBg(scene, 100)
    flowLight()

    // 波浪效果
    initFloor()
  }

  // 处理 GeoJson 地图
  const init3DMap = () => {
    // 循环显示 地图区域块
    xwqJson.features.forEach(item => {
      if (item.geometry.type == 'MultiPolygon') {
        item.geometry.coordinates.forEach(val => {
          val.forEach(v => renderMapChildren(v, '#b4dff3', item.properties))
        })
      }

      if (item.geometry.type == 'Polygon') {
        item.geometry.coordinates.forEach(val => renderMapChildren(val, '#b4dff3', item.properties))
      }
    })
  }

  // 生成 3D 地图
  const renderMapChildren = (mapData, color, properties) => {
    // initLightHao(properties)
    const map_block = new THREE.Object3D()
    const point = []
    const linePoints = []

    mapData.forEach(val => {
      const [x, y] = projection(val)
      point.push(new THREE.Vector3(x, -y, 2))
      linePoints.push(new THREE.Vector3(x, -y, 4))
      // mapEdgeLightObj.mapEdgePoints.push([x, -y, 0.2]);
    });

    initLabel(properties)
    
    const shape = new THREE.Shape(point)

    const extrudeSettings = {
      depth: 1.5,
      bevelEnabled: false,
      bevelThickness: 1,
      bevelSize: 1,
      bevelOffset: 0,
      bevelSegments: 1
    }

    // 地图区域
    const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings)

    // 贴图
    const textureMap = texture.load(`images/gz-map.jpg`)
    textureMap.wrapS = THREE.MirroredRepeatWrapping;
    textureMap.wrapT = THREE.MirroredRepeatWrapping;
    textureMap.repeat.set(0.1, 0.1);
    texture.magFilter = THREE.NearestFilter
    textureMap.rotation = THREE.MathUtils.degToRad(45)

    const material = new THREE.MeshPhongMaterial({
      map: textureMap,
      normalMap: textureMap,
      color,
      opacity: 1,
      combine: THREE.MultiplyOperation,
      transparent: true,
      depthTest: true,
      side: THREE.FrontSide
    })

    // 突出材料
    const sideMaterial = new THREE.MeshLambertMaterial({
      transparent: true,
      opacity: 0.5,
      side: THREE.FrontSide,
      bumpScale: 0.1,
      flatShading: true,
    })
    // sideMaterial.color.setStyle('#d4f806')
    const mesh = new THREE.Mesh(geometry, [material, sideMaterial])
    mesh.position.set(0, 0, 4.5)
    mesh.scale.set(1, 1, 1)
    mesh.name = properties.name
    mesh["isMapBlock"] = true
    map_block.add(mesh)


    // 地图区域边界线
    const lineGeometry = new THREE.BufferGeometry().setFromPoints(linePoints)
    const lineMaterial = new THREE.LineBasicMaterial({ 
      color: '#ffffff', 
      linewidth: 0.0026,
      transparent: true, 
      depthTest: true, 
      emissive: 0xffffff,     // 发光颜色
      emissiveIntensity: 15, // 发光强度
    });
    const line = new THREE.Line(lineGeometry, lineMaterial);
    line.scale.set(1, 1, 1.51)
    map_block.add(line);

    map_block.properties = properties
    scene.add(map_block)
  }

  // 坐标轴
  const initAxes = () => {
    let axes = new THREE.AxesHelper(1000)
    scene.add(axes)
  }

  // 光照
  const initLight = () => {
    const light = new THREE.PointLight(0xffffff, 1);
    light.position.set(-20, -10, 10);
    scene.add(light);

    // 光源辅助线
    const lightHelper = new THREE.PointLightHelper(light);
    scene.add(lightHelper);
  }

  // 涟漪
  const initLightHao = (properties) => {
    
    // 标记点：几何体，材质，
    const geometry = new THREE.PlaneGeometry(1, 1)
    const material = new THREE.MeshBasicMaterial({
      map: texture.load(`images/guang-quan.png`),
      color: 0xf806cc,
      side: THREE.DoubleSide,
      opacity: 0,
      transparent: true,
      depthWrite: false, //禁止写入深度缓冲区数据
    })
    let mesh = new THREE.Mesh(geometry, material)
    mesh.renderOrder = 98
    mesh.name = "createLightHalo"
    // 缩放
    const scale = 1.3
    mesh.scale.set(scale, scale, scale)
    // 动画延迟时间
    const delay = random(0, 2000)
    // 动画：透明度缩放动画
    mesh.tween1 = new TWEEN.Tween({ scale: scale, opacity: 0 })
      .to({ scale: scale * 1.5, opacity: 1 }, 1000)
      .delay(delay)
      .onUpdate((params) => {
        let { scale, opacity } = params
        mesh.scale.set(scale, scale, scale)
        mesh.material.opacity = opacity
      })
    mesh.tween2 = new TWEEN.Tween({ scale: scale * 1.5, opacity: 1 })
      .to({ scale: scale * 2, opacity: 0 }, 1000)
      .onUpdate((params) => {
        let { scale, opacity } = params
        mesh.scale.set(scale, scale, scale)
        mesh.material.opacity = opacity
      })
    mesh.tween1.chain(mesh.tween2)
    mesh.tween2.chain(mesh.tween1)
    mesh.tween1.start()

    let [x, y] = projection(properties.center)
    mesh.position.set(x, y, 6.5)
    scene.add(mesh)
  }

  const random = function (min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
  }

  // 鼠标 经过 地图 区域 
  const onPointerMove = (event) => {
    const raycaster = new THREE.Raycaster();
    const pointer = new THREE.Vector2();

    pointer.x = ( event.clientX / window.innerWidth ) * 2 - 1;
    pointer.y = - ( event.clientY / window.innerHeight ) * 2 + 1;

    // 通过摄像机和鼠标位置更新射线
    raycaster.setFromCamera(pointer, camera);
    if (hoverIntersects.length) {
      for (let i = 0; i < hoverIntersects.length; i++) {
        if (hoverIntersects[i].object.isMapBlock) {
          hoverIntersects[i].object.material[0].color.set('#b4dff3');
        }
      }
    }

    // 计算物体和射线的焦点
    const intersects = raycaster.intersectObjects(scene.children);

    // 显示 label
    // if (intersects.length && intersects[0].object.parent.properties) {
    //   label.innerHTML = intersects[0].object.parent.properties.name
    //   label.style.left = event.clientX + 'px'
    //   label.style.top = event.clientY + 'px'
    //   label.style.display = 'block'
    // } else {
    //   label.style.display = 'none'
    // }

    hoverIntersects = []

    for ( let i = 0; i < intersects.length; i ++ ) {
      if (intersects[i].object.type == 'Mesh' && intersects[i].object.isMapBlock) {
        hoverIntersects.push(intersects[i])
        intersects[i].object.material[0].color.set('#01A7D3');
      }
    }
  }

  // 点击 地图 区域
  const clickMaoBlock = (event) => {
    if (clickIntersects.length && clickIntersects[0].object.isMapBlock) {
      clickIntersects[0].object.position.z -= 1
    }

    // 将鼠标坐标 转换为 Threejs 中的坐标
    const mouse = new THREE.Vector2()
    mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
    mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;

    // 发射射线
    const rayCaster = new THREE.Raycaster()
    rayCaster.setFromCamera(mouse, camera)

    // 检测相交对象
    clickIntersects = rayCaster.intersectObjects(scene.children) 

    // 如果相交的是地图部分
    if (clickIntersects.length > 0 && clickIntersects[0].object.isMapBlock) {
      console.log('当前点击区域 : ', clickIntersects[0].object.name);
      let object = clickIntersects[0].object
      // object.material.color.set('#01A7D3');
      object.position.z += 1
    }
  }

  // 背景图 模糊图
  const initSceneBg = (scene, width) => {
    const img = texture.load(`images/scene-bg2.png`)
    let plane = new THREE.PlaneGeometry(width, width)
    let material = new THREE.MeshPhongMaterial({
      // color: 0x061920,
      color: 0xffffff,
      map: img,
      transparent: true,
      opacity: 1,
      depthTest: true,
    })

    let mesh = new THREE.Mesh(plane, material)
    mesh.position.set(0, 0, 1)
    scene.add(mesh)
  }

  // 背景图 点
  const initCirclePoint = (scene, width) => {
    let img = texture.load(`images/circle-point.png`)
    let plane = new THREE.PlaneGeometry(width, width)
    let material = new THREE.MeshPhongMaterial({
      // color: 0x00ffff,
      map: img,
      transparent: true,
      opacity: 1,
      // depthTest: false,
    })
    let mesh = new THREE.Mesh(plane, material)
    mesh.position.set(0, 0, 2)
    scene.add(mesh)
  }
  
  // 背景图 光圈1
  const initRotating1 = (scene, width) => {
    let img = texture.load(`images/rotatingAperture.png`)
    let plane = new THREE.PlaneGeometry(width, width)
    let material = new THREE.MeshBasicMaterial({
      map: img,
      transparent: true,
      opacity: 1,
      depthTest: true,
    })
    let mesh = new THREE.Mesh(plane, material)
    mesh.position.set(0, 0, 3)
    // mesh.scale.set(1, 1, 1)
    scene.add(mesh)
    return mesh
  }

  // 背景图 光圈2
  const initRotating2 = (scene, width) => {
    let img = texture.load(`images/rotating-point2.png`)
    let plane = new THREE.PlaneGeometry(width, width)
    let material = new THREE.MeshBasicMaterial({
      map: img,
      transparent: true,
      opacity: 1,
      depthTest: true,
    })
    let mesh = new THREE.Mesh(plane, material)
    mesh.position.set(0, 0, 4)
    // mesh.scale.set(1, 1, 1)
    scene.add(mesh)
    return mesh
  }

  // 地图边缘流光效果
  const setMapEdgeLight = () => {
    let positions = new Float32Array(mapEdgeLightObj.mapEdgePoints.flat(1));  // 数组深度遍历扁平化
    // console.log(positions);
    mapEdgeLightObj.lightOpacityGeometry = new THREE.BufferGeometry();
    // 设置顶点
    mapEdgeLightObj.lightOpacityGeometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
    // 设置 粒子透明度为 0
    mapEdgeLightObj.lightOpacitys = new Float32Array(positions.length).map(() => 0);
    mapEdgeLightObj.lightOpacityGeometry.setAttribute("aOpacity", new THREE.BufferAttribute(mapEdgeLightObj.lightOpacitys, 1));

    // 顶点着色器
    const vertexShader = `
      attribute float aOpacity;
      uniform float uSize;
      varying float vOpacity;

      void main(){
          gl_Position = projectionMatrix*modelViewMatrix*vec4(position,1.0);
          gl_PointSize = uSize;

          vOpacity=aOpacity;
      }
      `
    // 片段着色器
    const fragmentShader = `
      varying float vOpacity;
      uniform vec3 uColor;

      float invert(float n){
          return 1.-n;
      }

      void main(){
        if(vOpacity <=0.2){
            discard;
        }
        vec2 uv=vec2(gl_PointCoord.x,invert(gl_PointCoord.y));
        vec2 cUv=2.*uv-1.;
        vec4 color=vec4(1./length(cUv));
        color*=vOpacity;
        color.rgb*=uColor;
        gl_FragColor=color;
      }
      `
    
    const material = new THREE.ShaderMaterial({
      vertexShader: vertexShader,
      fragmentShader: fragmentShader,
      transparent: true, // 设置透明
      // blending: THREE.AdditiveBlending,
      uniforms: {
        uSize: {
          value: 5.0
        },
        uColor: {
          value: new THREE.Color("#ffffff")  // 光点颜色 fffb85
        }
      }
    })
    // material.blending = THREE.AdditiveBlending;
    const opacityPointsMesh = new THREE.Points(this.mapEdgeLightObj.lightOpacityGeometry, material);
    scene.add(opacityPointsMesh);
  }

  // 显示 地图区域块 label
  const initLabel = (properties) => {
    let center = properties.center
    let [x, y] = projection(center)
    let tag = document.createElement('div')
    tag.innerHTML = properties.name
    tag.style.color = '#ffffff'
    tag.style.position = 'absolute'
    tag.style.fontSize = '10'

    let label = new CSS2DObject(tag)
    label.element.style.visibility = 'visible'
    label.position.set(x, y, 7)
    scene.add(label)
  }

  // 分散效果
  const initFloor = () => {
    let vextureShader = `
      varying vec2 vUv;
      varying vec3 fNormal;
      varying vec3 vPosition;
      void main()
      {
      vUv = uv;
      fNormal=normal;
      vPosition=position;
      gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
      }
    `

    let fragmentShader = `
      varying vec2 vUv;
      uniform float iTime;
      uniform sampler2D colorTexture;
      vec3 hsb2rgb(const vec3 c) {
      vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0, 4.0, 2.0), 6.0)-3.0)-1.0, 0.0, 1.0);
        rgb = rgb*rgb*(2.0-2.0*rgb);
        return c.z * mix( vec3(1.0), rgb, c.y);
      }
      void main() {
        vec2 uv = vUv;
        vec2 p = 2.0*uv.xy - vec2(1., 1.) ; 
        float r = length(p) * 1.; //圈数量
        vec3 color = hsb2rgb(vec3(0.4, 1., 1.)); //0.24, 0.7, 0.5
        vec4 textureValue = texture2D( colorTexture, vUv );
        float a = pow(r, 2.0); //圈数量
        float b = sin(r * 0.8 - 1.6);
        float c = sin(r - .10);
        float s = sin(a - iTime * 3.0 + b) * c;
        color *= abs(1.0 / (s * 30.)) - 0.3; //初始圈大小
        gl_FragColor = vec4(color, 1.);
      }
    `

    //扩散波效果
    let circle_geometry = new THREE.CircleGeometry(40, 20);
    var material = new THREE.ShaderMaterial({
      uniforms: uniforms,
      vertexShader: vextureShader,
      fragmentShader: fragmentShader,
      side: THREE.DoubleSide,
      blending: THREE.AdditiveBlending,
      transparent: true,
      opacity: 0.5
    })
    let plane2 = new THREE.Mesh(circle_geometry, material);
    plane2.rotateX(-Math.PI)
    scene.add(plane2);
  }

  // 流光效果
  const flowLight = () => {
    const feature = xwqBroundaryJson.features[0]
    const province = new THREE.Object3D()
    province.properties = feature.properties.name
    // 点数据
    const coordinates = feature.geometry.coordinates

    coordinates.forEach(coordinate => {
      // coordinate 多边形数据
      coordinate.forEach(rows => {

        // 绘制 外边
        const line = lineDraw(rows, 0x00A295)
        province.add(line)
      })
    })

    positions = new Float32Array(lines.flat(1))
    // 设置顶点
    bufferGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3))
    // 设置 粒子透明度为 0
    opacitys = new Float32Array(positions.length).map(() => 0)
    bufferGeometry.setAttribute('aOpacity', new THREE.BufferAttribute(opacitys, 1))

    scene.add(province)

    function lineDraw(polygon, color) {
      const lineGeometry = new THREE.BufferGeometry()
      const pointsArray = new Array()

      polygon.forEach((row) => {
        const [x, y] = projection(row)
        // 创建三维点
        pointsArray.push(new THREE.Vector3(x-0.15, -y+0.1, 6))

        if (indexBol) {
          lines.push([x-0.15, -y+0.1, 6])
        }
      })

      indexBol = false
      // 放入多个点
      lineGeometry.setFromPoints(pointsArray)

      const lineMaterial = new THREE.LineBasicMaterial({
        color: color
      })
      return new THREE.Line(lineGeometry, lineMaterial)
    }

    // 控制 颜色和粒子大小
    const params = {
      pointSize: 5.0,
      pointColor: '#0632f8'
    }

    const vertexShader = `
      attribute float aOpacity;
      uniform float uSize;
      varying float vOpacity;

      void main(){
        gl_Position = projectionMatrix*modelViewMatrix*vec4(position,1.0);
        gl_PointSize = uSize;

        vOpacity=aOpacity;
      }
      `

    const fragmentShader = `
      varying float vOpacity;
      uniform vec3 uColor;

      float invert(float n){
        return 1.-n;
      }

      void main(){
        if(vOpacity <=0.8){
          discard;
        }
        vec2 uv=vec2(gl_PointCoord.x,invert(gl_PointCoord.y));
        vec2 cUv=2.*uv-1.;
        vec4 color=vec4(1./length(cUv));
        color.rgb*=uColor;
        gl_FragColor=color;
      }
      `
    const material = new THREE.ShaderMaterial({
      vertexShader: vertexShader,
      fragmentShader: fragmentShader,
      transparent: false, // 设置透明
      uniforms: {
        uSize: {
          value: params.pointSize
        },
        uColor: {
          value: new THREE.Color('#0632f8')
        }
      }
    })
    points = new THREE.Points(bufferGeometry, material)
    scene.add(points)
  }

  let currentPos = 0
  let pointSpeed = 2 // 速度
      
  const loop = () => {
    // 辉光效果
    const timer = clock.getDelta();
    uniforms.iTime.value += Math.sin(timer) / 3;
    if (uniforms.iTime.value > 1.0) {
        uniforms.iTime.value = 0.0;
    }

    // 流光效果
    if (points && bufferGeometry.attributes.position) {
      currentPos += pointSpeed
      for (let i = 0; i < currentPos; i++) {
        opacitys[(currentPos - i) % lines.length] = 0
      }

      for (let i = 0; i < 100; i++) {
        opacitys[(currentPos + i) % lines.length] = i / 50 > 2 ? 1 : i / 50
      }
      bufferGeometry.attributes.aOpacity.needsUpdate = true
    }

    requestAnimationFrame(loop);

    // 光圈 旋转
    if (img1.value) {
      img1.value.rotation.z += 0.002;
    }
    if (img2.value) {
      img2.value.rotation.z -= 0.002;
    }

    // 旋转地图
    // scene.rotation.z += 0.002;

    TWEEN.update()

    renderer.render(scene, camera)
    labelRenderer.render(scene, camera)
  }
  
  // 相机控件： 鼠标控制 旋转 缩放
  const controls = new OrbitControls(camera, renderer.domElement)
  controls.addEventListener('change', () => {
    renderer.render(scene, camera)
    // labelRenderer.render(scene, camera)
  })

  window.addEventListener('pointermove', onPointerMove);
  window.addEventListener('click', clickMaoBlock, false);
</script>

<style>
  #3d_map_box { width: 100%; height: 100%; }

  #label { position: absolute; top: 0; left: 0 }

  .s { color: #0632f8 }
  
  .map-32-label {
    font-size: 300px;
    color: #fff;
    font-weight: bold
  }
</style>
